# -*- coding: utf-8 -*-

import werkzeug.urls
import urlparse
import urllib2
import simplejson
import time

import openerp
from openerp.osv import osv, fields

SUPERUSER_ID = 1

#用户扩展实现微信回调验证
class res_users(osv.Model):
    _inherit = 'res.users'

    _columns = {
        #auth_oauth中已经有了用户id: 'oauth_uid': fields.char('OAuth User ID', help="Oauth Provider user_id", copy=False),
        #企业号微信中下面三个字段必须有一个匹配
        'weixin_id': fields.char(u'微信号', copy=False),
        #这里的手机号码只是冗余的记录一下，绑定的手机号要比对partner表中的手机号码
        'mobile': fields.char(u'微信手机号码', readonly=True, copy=False),
        #目前无用
        'email': fields.char(u'微信邮箱', readonly=True, copy=False),
    }

    def _auth_oauth_rpc(self, cr, uid, endpoint_url, params, context=None):
        params_suffix = werkzeug.url_encode(params)
        if urlparse.urlparse(endpoint_url)[4]:
            endpoint_url = endpoint_url + '&' + params_suffix
        else:
            endpoint_url = endpoint_url + '?' + params_suffix
        f = urllib2.urlopen(endpoint_url)
        response = f.read()
        return simplejson.loads(response)

    def _auth_oauth_validate(self, cr, uid, provider, code, agentid, context=None):
        """ return the validation data corresponding to the access token """
        provider_ids = self.pool.get('auth.oauth.provider').search(cr, uid, [('name', '=', provider)], context=context)
        auth_oauth_provider = self.pool.get('auth.oauth.provider').browse(cr, uid, provider_ids[0], context=context)
        validation = {"provider_id": provider_ids[0]}
        #检查access_token是否过期
        if time.time() - auth_oauth_provider.access_token_timestamp >= 7200:
            auth_oauth_provider.write({'access_token_timestamp': time.time()})
            data = self._auth_oauth_rpc(cr, uid, auth_oauth_provider.gettoken_endpoint
                                          , {'corpid': auth_oauth_provider.client_id, 'corpsecret': auth_oauth_provider.secret})
            #如果取得access_token成功就保存
            if not data.has_key("errcode"):
                auth_oauth_provider.access_token = data['access_token']
                auth_oauth_provider.write({'access_token': data['access_token']})
                validation.update(data)
        else:
            #access_token有效
            validation.update({'access_token': auth_oauth_provider.access_token})

        #检查jsapi_ticket是否过期
        if time.time() - auth_oauth_provider.jsapi_ticket_timestamp >= 7200:
            auth_oauth_provider.write({'jsapi_ticket_timestamp': time.time()})
            data = self._auth_oauth_rpc(cr, uid, 'https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket'
                                          , {'access_token': auth_oauth_provider.access_token})
            #如果取得jsapi_ticket成功就保存
            if data.get("errcode") == 0:
                auth_oauth_provider.jsapi_ticket = data['ticket']
                auth_oauth_provider.write({'jsapi_ticket': data['ticket']})
                validation.update({'jsapi_ticket': data['ticket']})
                auth_oauth_provider.write({'access_token_timestamp': time.time()})
        else:
            #access_token有效
            validation.update({'jsapi_ticket': auth_oauth_provider.jsapi_ticket})


        #https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo
        #取得微信服务器的用户UserId和DeviceId
        data = self._auth_oauth_rpc(cr, uid, auth_oauth_provider.validation_endpoint,
                                {'access_token': auth_oauth_provider.access_token, 'code': code, 'agentid': agentid})

        #可能用户没有在企业号中登记，成功的话没有errcode
        if not data.has_key("errcode"):
            validation.update(data)
        else:
            raise Exception(u'取得微信服务器企业用户UserId错误: %s' % data)

        #取得微信用户信息
        data = self._auth_oauth_rpc(cr, uid, auth_oauth_provider.data_endpoint
                                    , {'access_token': auth_oauth_provider.access_token, 'userid': validation.get('UserId')})
        #可能取不到用户信息，如果成功的话errcode等于0
        if data.has_key("errcode") and data['errcode'] == 0:
            validation.update(data)
        else:
            raise Exception(u'无法取得微信服务器企业用户基本信息: %s' % data)
        return validation

    def _auth_oauth_signin(self, cr, uid, provider_id, validation, params, context=None):
        """ retrieve and sign in the user corresponding to provider and validated access token
            :param provider_id: oauth provider id (int)
            :param validation: result of validation of access token (dict)
            :param params: oauth parameters (dict)
            :return: user login (str)
            :raise: openerp.exceptions.AccessDenied if signin failed

            This method can be overridden to add alternative signin methods.
        """
        try:
            user_ids = []
            #如果用户账号没有在微信企业号后台登记，是获取不到UserId的
            if validation.has_key('UserId'):
                oauth_uid = validation['UserId']
                user_ids = self.search(cr, uid, [("oauth_uid", "=", oauth_uid), ('oauth_provider_id', '=', provider_id)])

            if not user_ids:
                raise openerp.exceptions.AccessDenied()
            else:
                assert len(user_ids) == 1

            user = self.browse(cr, uid, user_ids[0], context=context)
            #用微信号做为用户访问token
            user.write({'oauth_access_token': validation['weixinid']})
            cr.commit()
            return user.login
        except openerp.exceptions.AccessDenied, access_denied_exception:
            if context and context.get('no_user_creation'):
                return None
            #raise access_denied_exception

    def auth_oauth(self, cr, uid, provider, params, context=None):
        # Advice by Google (to avoid Confused Deputy Problem)
        # if validation.audience != OUR_CLIENT_ID:
        #   abort()
        # else:
        #   continue with the process
        code = params.get('code')
        agentid = params.get('agentid')
        validation = self._auth_oauth_validate(cr, uid, provider, code, agentid)
        # required check
        #取不到UserId不应该抛出例外，而是需要先账号绑定，绑定不成功时提示需要跟相关人员联系
        #if not validation.get('UserId'):
        #    raise openerp.exceptions.AccessDenied()
        # retrieve and sign in user
        provider_id = validation['provider_id']
        #odoo登录帐号
        login_id = self._auth_oauth_signin(cr, uid, provider_id, validation, params, context=context)
        #企业用户id
        user_id = ''
        weixin_id = ''
        if validation.has_key('UserId'):
            user_id = validation['UserId']
            #微信用户id
            weixin_id = validation['weixinid']
        #oauth_access_token和access_token是不同的
        return {'dbname': cr.dbname, 'login_id': login_id, 'oauth_access_token': weixin_id,
                'oauth_uid': user_id, 'oauth_provider_id': provider_id, 'weixin_id': weixin_id}

    def check_credentials(self, cr, uid, password):
        try:
            return super(res_users, self).check_credentials(cr, uid, password)
        except openerp.exceptions.AccessDenied:
            #这里的password暂时是微信帐号
            res = self.search(cr, SUPERUSER_ID, [('id', '=', uid), ('oauth_access_token', '=', password)])
            if not res:
                raise


class auth_oauth_provider(osv.osv):
    _inherit = 'auth.oauth.provider'

    #client_id: APP_ID、
    _columns = {
        'access_token': fields.char(u'微信接口主动调用token'), #7200秒有效
        'access_token_timestamp': fields.integer(u'token起始时间戳'),
        'jsapi_ticket': fields.char(u'微信js sdk访问token'), #7200秒有效
        'jsapi_ticket_timestamp': fields.integer(u'js sdk token起始时间戳'),
        'gettoken_endpoint' : fields.char(u'取得token URL'), #取得token
        #'client_id': fields.char(u'微信接入APP ID'), #已经有的字段
        'secret': fields.char(u'开发者凭据密钥'), #开发者凭据密钥
        'AES_key': fields.char(u'微信AES key'), #微信AES加密key
        'agent_id': fields.char(u'应用模块ID'), #对应微信自定义的的“我的应用”ID
    }